#include "surbasic/surbasic.h"
#include "surbasic/Digestor.h"
#include "surbasic/Public_Define.h"
#include "surbasic/EciesHelper.h"
#include "surbasic/FileUploaderFactory.h"
#include "surbasic/FileDownloaderFactory.h"
#include "surbasic/XmlHelper.h"
using namespace SDBasic;
using namespace SDBasic::ecc;
using namespace SDBasic::code;
using namespace SDBasic::digestor;
namespace SDBasic {
void Init() { Request::CURL_Init(); }
void UnInit() { Request::CURL_UnInit(); }
bool GetUserSpaceInfo(const uint64_t& key, UserSpaceInfo& info) {
  std::string response;
  Request curl;
  curl.Init();
  AccessToken token;
  TokenManager::get().GetToken(key, token);
  SD_CODE ret = curl.OpenAPI_GetUserSpaceInfo(token, response);
  if (ret != SD_SUCCESSED) {
    return false;
  }
  CJsonParse json(response);
  if (json.GetUserSpaceInfo(info) == false) return false;
  return true;
}
uint64_t Login(const std::string& server, const std::string& user,
               const std::string& plainpwd, AccessToken& token) {
  std::string response;
  Request curl;
  curl.Init();
  std::string md5pwd = Digestor::HashPassword(plainpwd);
  SD_CODE ret = curl.Personal_Login(server, user, md5pwd, 15000, response);

  AccessToken temp_token;
  SDContext* ctx = &CTX;
  if (xml::ParseCheckAccountXml(response.c_str(), temp_token) == false)
    return 0;

  ByteArray pk;
  int iret = EciesHelper::TryParsePrivKey(temp_token.privatekey, plainpwd, &pk);
  if (iret == 1) {
    token.privatekey = pk.toUrlBase64Str();
  } else {
    log_callback(LogError, "failed to parse user`s private key\r\n");
    return 0;
  }
  token.account = user;
  token.server = temp_token.server;
  token.dbserver = temp_token.dbserver;
  token.svgserver = temp_token.svgserver;
  token.webserver = temp_token.webserver;

  token.user_department = temp_token.user_department;
  token.user_email = temp_token.user_email;
  token.user_name = temp_token.user_name;
  token.user_role = temp_token.user_role;
  token.user_space = temp_token.user_space;

  if (!temp_token.access_token.empty()) {
    token.access_token = temp_token.access_token;
    token.refresh_token = temp_token.refresh_token;
    token.resource_server = temp_token.resource_server;
    token.expires_in = temp_token.expires_in;
  }

  token.account = user;

  return TokenManager::get().SetToken(token);
}

uint64_t LoginEnt(const std::string& server, const std::string& user,
                  const std::string& plainpwd, AccessToken& token) {
  std::string response;
  Request curl;
  curl.Init();
  std::string md5pwd = Digestor::HashPassword(plainpwd);
  SD_CODE ret = curl.Ent_Login(server, user, md5pwd, 15000, response);
  if (ret != SD_SUCCESSED) {
    log_callback(LogError, "Ent_login error\r\n");
    return 0;
  } else {
    log_callback(LogInfo, "Ent_login successed.\r\n");
  }

  AccessToken temp_token;
  SDContext* ctx = &CTX;
  CJsonParse parse(response);
  if (!parse.IsValid() || !parse.Enterprise_GetAccessToken(&temp_token, ctx))
    log_callback(LogError, "failed to parse login response.\r\n");
  ByteArray pk;
  int iret = EciesHelper::TryParsePrivKey(temp_token.privatekey, plainpwd, &pk);
  if (iret == 1) {
    token.privatekey = pk.toUrlBase64Str();
  } else {
    log_callback(LogError, "failed to parse user`s private key\r\n");
    return 0;
  }
  token.account = user;
  token.server = temp_token.server;
  token.dbserver = temp_token.dbserver;
  token.svgserver = temp_token.svgserver;
  token.webserver = temp_token.webserver;

  token.user_department = temp_token.user_department;
  token.user_email = temp_token.user_email;
  token.user_name = temp_token.user_name;
  token.user_role = temp_token.user_role;
  token.user_space = temp_token.user_space;
  token.root_folder_id = temp_token.root_folder_id;

  if (!temp_token.access_token.empty()) {
    token.access_token = temp_token.access_token;
    token.refresh_token = temp_token.refresh_token;
    token.resource_server = temp_token.resource_server;
    token.expires_in = temp_token.expires_in;
  }

  return TokenManager::get().SetToken(token);
}
AccessToken GetToken(const uint64_t& key) {
  AccessToken token;
  TokenManager::get().GetToken(key, token);
  return token;
}

bool Upload(const uint64_t& key, const std::string& filepath,
            const std::string& parentid, const std::string& name,
            Timestamp modifytime, TreeEntity& node) {
  FileUploaderEnc* uploader = FileUploaderFactory::get().CreateUploader(key);
  SD_CODE ret = uploader->UploadFileByParentId(key, filepath, parentid, name,
                                               modifytime, SHARE_NONE, node);

  delete uploader;
  return (ret = SD_SUCCESSED);
}

bool Download(const uint64_t& key, const std::string& id,
              const std::string& path) {
  FileDownloaderEnc* d = FileDownloaderFactory::get().CreateDownloader(key);
  SD_CODE ret = d->Download(key, id, path);
  delete d;
  return (ret == SD_SUCCESSED ? true : false);
}

bool DownloadWithoutDecryption(const uint64_t& key, const std::string& id,
                               const std::string& path, std::string* src_md5,
                               std::string* aes_key) {
  FileDownloaderEnc* d = FileDownloaderFactory::get().CreateDownloader(key);
  SD_CODE ret = d->DownloadWithoutDecryption(key, id, path, src_md5, aes_key);
  delete d;
  return (ret == SD_SUCCESSED ? true : false);
}

bool RenameFile(uint64_t key, const std::string& id,
                const std::string& new_name, TreeEntity& node) {
  std::string response;
  Request curl;
  curl.Init();
  AccessToken token;
  TokenManager::get().GetToken(key, token);
  SD_CODE ret = curl.OpenAPI_RenameFile(token, id, new_name, response);
  if (ret != SD_SUCCESSED) {
    return false;
  }

  CJsonParse json(response);
  if (json.ParseNode(node) == false) return false;
  return true;
}

bool DeleteFile(uint64_t key, const std::string& id, TreeEntity& node) {
  std::string response;
  Request curl;
  curl.Init();
  AccessToken token;
  TokenManager::get().GetToken(key, token);
  SD_CODE ret = curl.OpenAPI_DeleteFile(token, id, response);

  return (ret == SD_SUCCESSED);
}

bool MoveFile(uint64_t key, const std::string& id, const std::string& new_pid,
              TreeEntity& node) {
  std::string response;
  Request curl;
  curl.Init();
  AccessToken token;
  TokenManager::get().GetToken(key, token);
  SD_CODE ret = curl.OpenAPI_MoveFile(token, id, new_pid, response, OVERWRITE);
  if (ret != SD_SUCCESSED) {
    return false;
  }

  CJsonParse json(response);
  if (json.ParseNode(node) == false) return false;
  return true;
}

bool CreateFolder(uint64_t key, const std::string& pid, const std::string& name,
                  TreeEntity& node) {
  std::string response;
  Request curl;
  curl.Init();
  AccessToken token;
  TokenManager::get().GetToken(key, token);
  SD_CODE ret =
      curl.OpenAPI_CreateFolder(token, pid, name, response, OVERWRITE);
  if (ret != SD_SUCCESSED) {
    return false;
  }

  CJsonParse json(response);
  if (json.ParseNode(node) == false) return false;
  return true;
}

bool DeleteFolder(uint64_t key, const std::string& id, bool recursive) {
  std::string response;
  Request curl;
  curl.Init();
  AccessToken token;
  TokenManager::get().GetToken(key, token);
  SD_CODE ret = curl.OpenAPI_DeleteFolder(token, id, recursive, response);

  return (ret == SD_SUCCESSED);
}

bool RenameFolder(uint64_t key, const std::string& id,
                  const std::string& new_name, TreeEntity& node) {
  std::string response;
  Request curl;
  curl.Init();
  AccessToken token;
  TokenManager::get().GetToken(key, token);
  SD_CODE ret = curl.OpenAPI_RenameFolder(token, id, new_name, response);
  if (ret != SD_SUCCESSED) {
    return false;
  }

  CJsonParse json(response);
  if (json.ParseNode(node) == false) return false;
  return true;
}

bool GetFileChangeLists(uint64_t key, Timestamp lastsynctime, int offset,
                        int limit, bool show_del, DmvChangeList* list) {
  std::string response;
  Request curl;
  curl.Init();
  AccessToken token;
  TokenManager::get().GetToken(key, token);
  SD_CODE ret = curl.OpenAPI_GetFileChanges(token, lastsynctime, offset, limit,
                                            response, show_del);
  if (ret != SD_SUCCESSED) return false;

  CJsonParse json(response);
  if (json.GetFileChangeList(*list, Timestamp::Now()) == false) return false;

  return true;
}

bool GetFolderChangeLists(uint64_t key, Timestamp lastsynctime, int offset,
                          int limit, bool show_del, DmvChangeList* list) {
  std::string response;
  Request curl;
  curl.Init();
  AccessToken token;
  TokenManager::get().GetToken(key, token);
  SD_CODE ret = curl.OpenAPI_GetFolderChanges(token, lastsynctime, offset,
                                              limit, response, show_del);
  if (ret != SD_SUCCESSED) return false;

  CJsonParse json(response);
  if (json.GetFolderChangeList(*list, Timestamp::Now()) == false) return false;

  return true;
}

int LogToConsole(SDLogLevel level, const char* fmt, ...) {
  va_list ap;
  va_start(ap, fmt);
  ::vprintf(fmt, ap);
  va_end(ap);
  return 0;
}

LogCallback log_callback = LogToConsole;

SD_CODE GetSameHashList(uint64_t surdoc_key, const std::string& src_digest,
                        std::vector<SameHashFileInfo>* list) {
  curl::Request req;
  req.Init();
  AccessToken token;
  TokenManager::get().GetToken(surdoc_key, token);
  SD_CODE ret;

  std::string response;
  ret = req.Enc_CheckIfSameHashFileExists(token, src_digest, response);
  switch (ret) {
    case SD_OPENAPI_FILE_NOT_FOUND: { return SD_FILE_NEED_UPLOAD; }
    case SD_SUCCESSED:
      goto check_share_key_array;
    default: { return SD_FAILED; }
  }

check_share_key_array:
  CJsonParse parser(response);
  if (!parser.IsValid()) return SD_FAILED;
  if (parser.Enc_GetShareKeyArray(*list) == false) return SD_FAILED;

  if (list->empty()) return SD_FILE_NEED_UPLOAD;

  return SD_SUCCESSED;
}

}  // namespace
